/*
 * ProGuard -- shrinking, optimization, obfuscation, and preverification of Java bytecode.
 *
 * Copyright (c) 2002-2018 GuardSquare NV
 *
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this program; if not, write to the Free
 * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */
package proguard.classfile.editor;

import proguard.classfile.*;
import proguard.classfile.attribute.*;
import proguard.classfile.attribute.visitor.AttributeVisitor;
import proguard.classfile.constant.Constant;
import proguard.classfile.instruction.*;
import proguard.classfile.util.*;

import static proguard.classfile.ClassConstants.*;

/**
 * This AttributeVisitor accumulates instructions and exceptions, and then copies them into code attributes that it
 * visits.
 *
 * @see CodeAttributeComposer
 *
 * @author Eric Lafortune
 */
public class CompactCodeAttributeComposer extends SimplifiedVisitor implements AttributeVisitor {
    private final ConstantPoolEditor constantPoolEditor;
    private final CodeAttributeComposer codeAttributeComposer;

    /**
     * Creates a new CompactCodeAttributeComposer that doesn't allow external branch targets or exception handlers and
     * that automatically shrinks instructions.
     */
    public CompactCodeAttributeComposer(ProgramClass targetClass) {
        this(targetClass, false, false, true);
    }

    /**
     * Creates a new CompactCodeAttributeComposer.
     * 
     * @param allowExternalBranchTargets specifies whether branch targets can lie outside the code fragment of the
     *        branch instructions.
     * @param allowExternalExceptionHandlers specifies whether exception handlers can lie outside the code fragment in
     *        which exceptions are defined.
     * @param shrinkInstructions specifies whether instructions should automatically be shrunk before being written.
     */
    public CompactCodeAttributeComposer(ProgramClass targetClass, boolean allowExternalBranchTargets,
        boolean allowExternalExceptionHandlers, boolean shrinkInstructions) {
        constantPoolEditor = new ConstantPoolEditor(targetClass);

        codeAttributeComposer =
            new CodeAttributeComposer(allowExternalBranchTargets, allowExternalExceptionHandlers, shrinkInstructions);
    }

    /**
     * Starts a new code definition.
     */
    public CompactCodeAttributeComposer reset() {
        codeAttributeComposer.reset();

        return this;
    }

    /**
     * Starts a new code fragment. Branch instructions that are added are assumed to be relative within such code
     * fragments.
     * 
     * @param maximumCodeFragmentLength the maximum length of the code that will be added as part of this fragment (more
     *        precisely, the maximum old instruction offset or label that is specified, plus one).
     */
    public CompactCodeAttributeComposer beginCodeFragment(int maximumCodeFragmentLength) {
        codeAttributeComposer.beginCodeFragment(maximumCodeFragmentLength);

        return this;
    }

    /**
     * Appends the given instruction with the given old offset. Branch instructions must fit, for instance by enabling
     * automatic shrinking of instructions.
     * 
     * @param oldInstructionOffset the old offset of the instruction, to which branches and other references in the
     *        current code fragment are pointing.
     * @param instruction the instruction to be appended.
     */
    public CompactCodeAttributeComposer appendInstruction(int oldInstructionOffset, Instruction instruction) {
        codeAttributeComposer.appendInstruction(oldInstructionOffset, instruction);

        return this;
    }

    /**
     * Appends the given label with the given old offset.
     * 
     * @param oldInstructionOffset the old offset of the label, to which branches and other references in the current
     *        code fragment are pointing.
     */
    public CompactCodeAttributeComposer appendLabel(int oldInstructionOffset) {
        codeAttributeComposer.appendLabel(oldInstructionOffset);

        return this;
    }

    /**
     * Appends the given instruction without defined offsets.
     * 
     * @param instructions the instructions to be appended.
     */
    public CompactCodeAttributeComposer appendInstructions(Instruction[] instructions) {
        codeAttributeComposer.appendInstructions(instructions);

        return this;
    }

    /**
     * Appends the given instruction without a defined offset. Branch instructions should have a label, to allow
     * computing the new relative offset. Branch instructions must fit, for instance by enabling automatic shrinking of
     * instructions.
     * 
     * @param instruction the instruction to be appended.
     */
    public CompactCodeAttributeComposer appendInstruction(Instruction instruction) {
        codeAttributeComposer.appendInstruction(instruction);

        return this;
    }

    /**
     * Appends the given exception to the exception table.
     * 
     * @param exceptionInfo the exception to be appended.
     */
    public CompactCodeAttributeComposer appendException(ExceptionInfo exceptionInfo) {
        codeAttributeComposer.appendException(exceptionInfo);

        return this;
    }

    /**
     * Inserts the given line number at the appropriate position in the line number table.
     * 
     * @param lineNumberInfo the line number to be inserted.
     * @return the index where the line number was actually inserted.
     */
    public int insertLineNumber(LineNumberInfo lineNumberInfo) {
        return codeAttributeComposer.insertLineNumber(lineNumberInfo);
    }

    /**
     * Inserts the given line number at the appropriate position in the line number table.
     * 
     * @param minimumIndex the minimum index where the line number may be inserted.
     * @param lineNumberInfo the line number to be inserted.
     * @return the index where the line number was inserted.
     */
    public int insertLineNumber(int minimumIndex, LineNumberInfo lineNumberInfo) {
        return codeAttributeComposer.insertLineNumber(minimumIndex, lineNumberInfo);
    }

    /**
     * Appends the given line number to the line number table.
     * 
     * @param lineNumberInfo the line number to be appended.
     */
    public CompactCodeAttributeComposer appendLineNumber(LineNumberInfo lineNumberInfo) {
        codeAttributeComposer.appendLineNumber(lineNumberInfo);

        return this;
    }

    /**
     * Wraps up the current code fragment, continuing with the previous one on the stack.
     */
    public CompactCodeAttributeComposer endCodeFragment() {
        codeAttributeComposer.endCodeFragment();

        return this;
    }

    // Methods corresponding to the bytecode opcodes.

    public CompactCodeAttributeComposer nop() {
        return add(new SimpleInstruction(InstructionConstants.OP_NOP));
    }

    public CompactCodeAttributeComposer aconst_null() {
        return add(new SimpleInstruction(InstructionConstants.OP_ACONST_NULL));
    }

    public CompactCodeAttributeComposer iconst(int constant) {
        return add(new SimpleInstruction(InstructionConstants.OP_ICONST_0, constant));
    }

    public CompactCodeAttributeComposer iconst_m1() {
        return add(new SimpleInstruction(InstructionConstants.OP_ICONST_M1));
    }

    public CompactCodeAttributeComposer iconst_0() {
        return add(new SimpleInstruction(InstructionConstants.OP_ICONST_0));
    }

    public CompactCodeAttributeComposer iconst_1() {
        return add(new SimpleInstruction(InstructionConstants.OP_ICONST_1));
    }

    public CompactCodeAttributeComposer iconst_2() {
        return add(new SimpleInstruction(InstructionConstants.OP_ICONST_2));
    }

    public CompactCodeAttributeComposer iconst_3() {
        return add(new SimpleInstruction(InstructionConstants.OP_ICONST_3));
    }

    public CompactCodeAttributeComposer iconst_4() {
        return add(new SimpleInstruction(InstructionConstants.OP_ICONST_4));
    }

    public CompactCodeAttributeComposer iconst_5() {
        return add(new SimpleInstruction(InstructionConstants.OP_ICONST_5));
    }

    public CompactCodeAttributeComposer lconst(int constant) {
        return add(new SimpleInstruction(InstructionConstants.OP_LCONST_0, constant));
    }

    public CompactCodeAttributeComposer lconst_0() {
        return add(new SimpleInstruction(InstructionConstants.OP_LCONST_0));
    }

    public CompactCodeAttributeComposer lconst_1() {
        return add(new SimpleInstruction(InstructionConstants.OP_LCONST_1));
    }

    public CompactCodeAttributeComposer fconst(int constant) {
        return add(new SimpleInstruction(InstructionConstants.OP_FCONST_0, constant));
    }

    public CompactCodeAttributeComposer fconst_0() {
        return add(new SimpleInstruction(InstructionConstants.OP_FCONST_0));
    }

    public CompactCodeAttributeComposer fconst_1() {
        return add(new SimpleInstruction(InstructionConstants.OP_FCONST_1));
    }

    public CompactCodeAttributeComposer fconst_2() {
        return add(new SimpleInstruction(InstructionConstants.OP_FCONST_2));
    }

    public CompactCodeAttributeComposer dconst(int constant) {
        return add(new SimpleInstruction(InstructionConstants.OP_DCONST_0, constant));
    }

    public CompactCodeAttributeComposer dconst_0() {
        return add(new SimpleInstruction(InstructionConstants.OP_DCONST_0));
    }

    public CompactCodeAttributeComposer dconst_1() {
        return add(new SimpleInstruction(InstructionConstants.OP_DCONST_1));
    }

    public CompactCodeAttributeComposer bipush(int constant) {
        return add(new SimpleInstruction(InstructionConstants.OP_BIPUSH, constant));
    }

    public CompactCodeAttributeComposer sipush(int constant) {
        return add(new SimpleInstruction(InstructionConstants.OP_SIPUSH, constant));
    }

    public CompactCodeAttributeComposer ldc(int value) {
        return ldc_(constantPoolEditor.addIntegerConstant(value));
    }

    public CompactCodeAttributeComposer ldc(float value) {
        return ldc_(constantPoolEditor.addFloatConstant(value));
    }

    public CompactCodeAttributeComposer ldc(String string) {
        return ldc(string, null, null);
    }

    public CompactCodeAttributeComposer ldc(Object primitiveArray) {
        return ldc_(constantPoolEditor.addPrimitiveArrayConstant(primitiveArray));
    }

    public CompactCodeAttributeComposer ldc(String string, Clazz referencedClass, Method referencedMember) {
        return ldc_(constantPoolEditor.addStringConstant(string, referencedClass, referencedMember));
    }

    public CompactCodeAttributeComposer ldc(String className, Clazz referencedClass) {
        return ldc_(constantPoolEditor.addClassConstant(className, referencedClass));
    }

    public CompactCodeAttributeComposer ldc_(int constantIndex) {
        return add(new ConstantInstruction(InstructionConstants.OP_LDC, constantIndex));
    }

    public CompactCodeAttributeComposer ldc_w(int value) {
        return ldc_w_(constantPoolEditor.addIntegerConstant(value));
    }

    public CompactCodeAttributeComposer ldc_w(float value) {
        return ldc_w_(constantPoolEditor.addFloatConstant(value));
    }

    public CompactCodeAttributeComposer ldc_w(String string) {
        return ldc_w(string, null, null);
    }

    public CompactCodeAttributeComposer ldc_w(String string, Clazz referencedClass, Method referencedMember) {
        return ldc_w_(constantPoolEditor.addStringConstant(string, referencedClass, referencedMember));
    }

    public CompactCodeAttributeComposer ldc_w(String className, Clazz referencedClass) {
        return ldc_w_(constantPoolEditor.addClassConstant(className, referencedClass));
    }

    public CompactCodeAttributeComposer ldc_w_(int constantIndex) {
        return add(new ConstantInstruction(InstructionConstants.OP_LDC_W, constantIndex));
    }

    public CompactCodeAttributeComposer ldc2_w(long value) {
        return ldc2_w(constantPoolEditor.addLongConstant(value));
    }

    public CompactCodeAttributeComposer ldc2_w(double value) {
        return ldc2_w(constantPoolEditor.addDoubleConstant(value));
    }

    public CompactCodeAttributeComposer ldc2_w(int constantIndex) {
        return add(new ConstantInstruction(InstructionConstants.OP_LDC2_W, constantIndex));
    }

    public CompactCodeAttributeComposer iload(int variableIndex) {
        return add(new VariableInstruction(InstructionConstants.OP_ILOAD, variableIndex));
    }

    public CompactCodeAttributeComposer lload(int variableIndex) {
        return add(new VariableInstruction(InstructionConstants.OP_LLOAD, variableIndex));
    }

    public CompactCodeAttributeComposer fload(int variableIndex) {
        return add(new VariableInstruction(InstructionConstants.OP_FLOAD, variableIndex));
    }

    public CompactCodeAttributeComposer dload(int variableIndex) {
        return add(new VariableInstruction(InstructionConstants.OP_DLOAD, variableIndex));
    }

    public CompactCodeAttributeComposer aload(int variableIndex) {
        return add(new VariableInstruction(InstructionConstants.OP_ALOAD, variableIndex));
    }

    public CompactCodeAttributeComposer iload_0() {
        return add(new VariableInstruction(InstructionConstants.OP_ILOAD_0));
    }

    public CompactCodeAttributeComposer iload_1() {
        return add(new VariableInstruction(InstructionConstants.OP_ILOAD_1));
    }

    public CompactCodeAttributeComposer iload_2() {
        return add(new VariableInstruction(InstructionConstants.OP_ILOAD_2));
    }

    public CompactCodeAttributeComposer iload_3() {
        return add(new VariableInstruction(InstructionConstants.OP_ILOAD_3));
    }

    public CompactCodeAttributeComposer lload_0() {
        return add(new VariableInstruction(InstructionConstants.OP_LLOAD_0));
    }

    public CompactCodeAttributeComposer lload_1() {
        return add(new VariableInstruction(InstructionConstants.OP_LLOAD_1));
    }

    public CompactCodeAttributeComposer lload_2() {
        return add(new VariableInstruction(InstructionConstants.OP_LLOAD_2));
    }

    public CompactCodeAttributeComposer lload_3() {
        return add(new VariableInstruction(InstructionConstants.OP_LLOAD_3));
    }

    public CompactCodeAttributeComposer fload_0() {
        return add(new VariableInstruction(InstructionConstants.OP_FLOAD_0));
    }

    public CompactCodeAttributeComposer fload_1() {
        return add(new VariableInstruction(InstructionConstants.OP_FLOAD_1));
    }

    public CompactCodeAttributeComposer fload_2() {
        return add(new VariableInstruction(InstructionConstants.OP_FLOAD_2));
    }

    public CompactCodeAttributeComposer fload_3() {
        return add(new VariableInstruction(InstructionConstants.OP_FLOAD_3));
    }

    public CompactCodeAttributeComposer dload_0() {
        return add(new VariableInstruction(InstructionConstants.OP_DLOAD_0));
    }

    public CompactCodeAttributeComposer dload_1() {
        return add(new VariableInstruction(InstructionConstants.OP_DLOAD_1));
    }

    public CompactCodeAttributeComposer dload_2() {
        return add(new VariableInstruction(InstructionConstants.OP_DLOAD_2));
    }

    public CompactCodeAttributeComposer dload_3() {
        return add(new VariableInstruction(InstructionConstants.OP_DLOAD_3));
    }

    public CompactCodeAttributeComposer aload_0() {
        return add(new VariableInstruction(InstructionConstants.OP_ALOAD_0));
    }

    public CompactCodeAttributeComposer aload_1() {
        return add(new VariableInstruction(InstructionConstants.OP_ALOAD_1));
    }

    public CompactCodeAttributeComposer aload_2() {
        return add(new VariableInstruction(InstructionConstants.OP_ALOAD_2));
    }

    public CompactCodeAttributeComposer aload_3() {
        return add(new VariableInstruction(InstructionConstants.OP_ALOAD_3));
    }

    public CompactCodeAttributeComposer iaload() {
        return add(new SimpleInstruction(InstructionConstants.OP_IALOAD));
    }

    public CompactCodeAttributeComposer laload() {
        return add(new SimpleInstruction(InstructionConstants.OP_LALOAD));
    }

    public CompactCodeAttributeComposer faload() {
        return add(new SimpleInstruction(InstructionConstants.OP_FALOAD));
    }

    public CompactCodeAttributeComposer daload() {
        return add(new SimpleInstruction(InstructionConstants.OP_DALOAD));
    }

    public CompactCodeAttributeComposer aaload() {
        return add(new SimpleInstruction(InstructionConstants.OP_AALOAD));
    }

    public CompactCodeAttributeComposer baload() {
        return add(new SimpleInstruction(InstructionConstants.OP_BALOAD));
    }

    public CompactCodeAttributeComposer caload() {
        return add(new SimpleInstruction(InstructionConstants.OP_CALOAD));
    }

    public CompactCodeAttributeComposer saload() {
        return add(new SimpleInstruction(InstructionConstants.OP_SALOAD));
    }

    public CompactCodeAttributeComposer istore(int variableIndex) {
        return add(new VariableInstruction(InstructionConstants.OP_ISTORE, variableIndex));
    }

    public CompactCodeAttributeComposer lstore(int variableIndex) {
        return add(new VariableInstruction(InstructionConstants.OP_LSTORE, variableIndex));
    }

    public CompactCodeAttributeComposer fstore(int variableIndex) {
        return add(new VariableInstruction(InstructionConstants.OP_FSTORE, variableIndex));
    }

    public CompactCodeAttributeComposer dstore(int variableIndex) {
        return add(new VariableInstruction(InstructionConstants.OP_DSTORE, variableIndex));
    }

    public CompactCodeAttributeComposer astore(int variableIndex) {
        return add(new VariableInstruction(InstructionConstants.OP_ASTORE, variableIndex));
    }

    public CompactCodeAttributeComposer istore_0() {
        return add(new VariableInstruction(InstructionConstants.OP_ISTORE_0));
    }

    public CompactCodeAttributeComposer istore_1() {
        return add(new VariableInstruction(InstructionConstants.OP_ISTORE_1));
    }

    public CompactCodeAttributeComposer istore_2() {
        return add(new VariableInstruction(InstructionConstants.OP_ISTORE_2));
    }

    public CompactCodeAttributeComposer istore_3() {
        return add(new VariableInstruction(InstructionConstants.OP_ISTORE_3));
    }

    public CompactCodeAttributeComposer lstore_0() {
        return add(new VariableInstruction(InstructionConstants.OP_LSTORE_0));
    }

    public CompactCodeAttributeComposer lstore_1() {
        return add(new VariableInstruction(InstructionConstants.OP_LSTORE_1));
    }

    public CompactCodeAttributeComposer lstore_2() {
        return add(new VariableInstruction(InstructionConstants.OP_LSTORE_2));
    }

    public CompactCodeAttributeComposer lstore_3() {
        return add(new VariableInstruction(InstructionConstants.OP_LSTORE_3));
    }

    public CompactCodeAttributeComposer fstore_0() {
        return add(new VariableInstruction(InstructionConstants.OP_FSTORE_0));
    }

    public CompactCodeAttributeComposer fstore_1() {
        return add(new VariableInstruction(InstructionConstants.OP_FSTORE_1));
    }

    public CompactCodeAttributeComposer fstore_2() {
        return add(new VariableInstruction(InstructionConstants.OP_FSTORE_2));
    }

    public CompactCodeAttributeComposer fstore_3() {
        return add(new VariableInstruction(InstructionConstants.OP_FSTORE_3));
    }

    public CompactCodeAttributeComposer dstore_0() {
        return add(new VariableInstruction(InstructionConstants.OP_DSTORE_0));
    }

    public CompactCodeAttributeComposer dstore_1() {
        return add(new VariableInstruction(InstructionConstants.OP_DSTORE_1));
    }

    public CompactCodeAttributeComposer dstore_2() {
        return add(new VariableInstruction(InstructionConstants.OP_DSTORE_2));
    }

    public CompactCodeAttributeComposer dstore_3() {
        return add(new VariableInstruction(InstructionConstants.OP_DSTORE_3));
    }

    public CompactCodeAttributeComposer astore_0() {
        return add(new VariableInstruction(InstructionConstants.OP_ASTORE_0));
    }

    public CompactCodeAttributeComposer astore_1() {
        return add(new VariableInstruction(InstructionConstants.OP_ASTORE_1));
    }

    public CompactCodeAttributeComposer astore_2() {
        return add(new VariableInstruction(InstructionConstants.OP_ASTORE_2));
    }

    public CompactCodeAttributeComposer astore_3() {
        return add(new VariableInstruction(InstructionConstants.OP_ASTORE_3));
    }

    public CompactCodeAttributeComposer iastore() {
        return add(new SimpleInstruction(InstructionConstants.OP_IASTORE));
    }

    public CompactCodeAttributeComposer lastore() {
        return add(new SimpleInstruction(InstructionConstants.OP_LASTORE));
    }

    public CompactCodeAttributeComposer fastore() {
        return add(new SimpleInstruction(InstructionConstants.OP_FASTORE));
    }

    public CompactCodeAttributeComposer dastore() {
        return add(new SimpleInstruction(InstructionConstants.OP_DASTORE));
    }

    public CompactCodeAttributeComposer aastore() {
        return add(new SimpleInstruction(InstructionConstants.OP_AASTORE));
    }

    public CompactCodeAttributeComposer bastore() {
        return add(new SimpleInstruction(InstructionConstants.OP_BASTORE));
    }

    public CompactCodeAttributeComposer castore() {
        return add(new SimpleInstruction(InstructionConstants.OP_CASTORE));
    }

    public CompactCodeAttributeComposer sastore() {
        return add(new SimpleInstruction(InstructionConstants.OP_SASTORE));
    }

    public CompactCodeAttributeComposer pop() {
        return add(new SimpleInstruction(InstructionConstants.OP_POP));
    }

    public CompactCodeAttributeComposer pop2() {
        return add(new SimpleInstruction(InstructionConstants.OP_POP2));
    }

    public CompactCodeAttributeComposer dup() {
        return add(new SimpleInstruction(InstructionConstants.OP_DUP));
    }

    public CompactCodeAttributeComposer dup_x1() {
        return add(new SimpleInstruction(InstructionConstants.OP_DUP_X1));
    }

    public CompactCodeAttributeComposer dup_x2() {
        return add(new SimpleInstruction(InstructionConstants.OP_DUP_X2));
    }

    public CompactCodeAttributeComposer dup2() {
        return add(new SimpleInstruction(InstructionConstants.OP_DUP2));
    }

    public CompactCodeAttributeComposer dup2_x1() {
        return add(new SimpleInstruction(InstructionConstants.OP_DUP2_X1));
    }

    public CompactCodeAttributeComposer dup2_x2() {
        return add(new SimpleInstruction(InstructionConstants.OP_DUP2_X2));
    }

    public CompactCodeAttributeComposer swap() {
        return add(new SimpleInstruction(InstructionConstants.OP_SWAP));
    }

    public CompactCodeAttributeComposer iadd() {
        return add(new SimpleInstruction(InstructionConstants.OP_IADD));
    }

    public CompactCodeAttributeComposer ladd() {
        return add(new SimpleInstruction(InstructionConstants.OP_LADD));
    }

    public CompactCodeAttributeComposer fadd() {
        return add(new SimpleInstruction(InstructionConstants.OP_FADD));
    }

    public CompactCodeAttributeComposer dadd() {
        return add(new SimpleInstruction(InstructionConstants.OP_DADD));
    }

    public CompactCodeAttributeComposer isub() {
        return add(new SimpleInstruction(InstructionConstants.OP_ISUB));
    }

    public CompactCodeAttributeComposer lsub() {
        return add(new SimpleInstruction(InstructionConstants.OP_LSUB));
    }

    public CompactCodeAttributeComposer fsub() {
        return add(new SimpleInstruction(InstructionConstants.OP_FSUB));
    }

    public CompactCodeAttributeComposer dsub() {
        return add(new SimpleInstruction(InstructionConstants.OP_DSUB));
    }

    public CompactCodeAttributeComposer imul() {
        return add(new SimpleInstruction(InstructionConstants.OP_IMUL));
    }

    public CompactCodeAttributeComposer lmul() {
        return add(new SimpleInstruction(InstructionConstants.OP_LMUL));
    }

    public CompactCodeAttributeComposer fmul() {
        return add(new SimpleInstruction(InstructionConstants.OP_FMUL));
    }

    public CompactCodeAttributeComposer dmul() {
        return add(new SimpleInstruction(InstructionConstants.OP_DMUL));
    }

    public CompactCodeAttributeComposer idiv() {
        return add(new SimpleInstruction(InstructionConstants.OP_IDIV));
    }

    public CompactCodeAttributeComposer ldiv() {
        return add(new SimpleInstruction(InstructionConstants.OP_LDIV));
    }

    public CompactCodeAttributeComposer fdiv() {
        return add(new SimpleInstruction(InstructionConstants.OP_FDIV));
    }

    public CompactCodeAttributeComposer ddiv() {
        return add(new SimpleInstruction(InstructionConstants.OP_DDIV));
    }

    public CompactCodeAttributeComposer irem() {
        return add(new SimpleInstruction(InstructionConstants.OP_IREM));
    }

    public CompactCodeAttributeComposer lrem() {
        return add(new SimpleInstruction(InstructionConstants.OP_LREM));
    }

    public CompactCodeAttributeComposer frem() {
        return add(new SimpleInstruction(InstructionConstants.OP_FREM));
    }

    public CompactCodeAttributeComposer drem() {
        return add(new SimpleInstruction(InstructionConstants.OP_DREM));
    }

    public CompactCodeAttributeComposer ineg() {
        return add(new SimpleInstruction(InstructionConstants.OP_INEG));
    }

    public CompactCodeAttributeComposer lneg() {
        return add(new SimpleInstruction(InstructionConstants.OP_LNEG));
    }

    public CompactCodeAttributeComposer fneg() {
        return add(new SimpleInstruction(InstructionConstants.OP_FNEG));
    }

    public CompactCodeAttributeComposer dneg() {
        return add(new SimpleInstruction(InstructionConstants.OP_DNEG));
    }

    public CompactCodeAttributeComposer ishl() {
        return add(new SimpleInstruction(InstructionConstants.OP_ISHL));
    }

    public CompactCodeAttributeComposer lshl() {
        return add(new SimpleInstruction(InstructionConstants.OP_LSHL));
    }

    public CompactCodeAttributeComposer ishr() {
        return add(new SimpleInstruction(InstructionConstants.OP_ISHR));
    }

    public CompactCodeAttributeComposer lshr() {
        return add(new SimpleInstruction(InstructionConstants.OP_LSHR));
    }

    public CompactCodeAttributeComposer iushr() {
        return add(new SimpleInstruction(InstructionConstants.OP_IUSHR));
    }

    public CompactCodeAttributeComposer lushr() {
        return add(new SimpleInstruction(InstructionConstants.OP_LUSHR));
    }

    public CompactCodeAttributeComposer iand() {
        return add(new SimpleInstruction(InstructionConstants.OP_IAND));
    }

    public CompactCodeAttributeComposer land() {
        return add(new SimpleInstruction(InstructionConstants.OP_LAND));
    }

    public CompactCodeAttributeComposer ior() {
        return add(new SimpleInstruction(InstructionConstants.OP_IOR));
    }

    public CompactCodeAttributeComposer lor() {
        return add(new SimpleInstruction(InstructionConstants.OP_LOR));
    }

    public CompactCodeAttributeComposer ixor() {
        return add(new SimpleInstruction(InstructionConstants.OP_IXOR));
    }

    public CompactCodeAttributeComposer lxor() {
        return add(new SimpleInstruction(InstructionConstants.OP_LXOR));
    }

    public CompactCodeAttributeComposer iinc(int variableIndex, int constant) {
        return add(new VariableInstruction(InstructionConstants.OP_IINC, variableIndex, constant));
    }

    public CompactCodeAttributeComposer i2l() {
        return add(new SimpleInstruction(InstructionConstants.OP_I2L));
    }

    public CompactCodeAttributeComposer i2f() {
        return add(new SimpleInstruction(InstructionConstants.OP_I2F));
    }

    public CompactCodeAttributeComposer i2d() {
        return add(new SimpleInstruction(InstructionConstants.OP_I2D));
    }

    public CompactCodeAttributeComposer l2i() {
        return add(new SimpleInstruction(InstructionConstants.OP_L2I));
    }

    public CompactCodeAttributeComposer l2f() {
        return add(new SimpleInstruction(InstructionConstants.OP_L2F));
    }

    public CompactCodeAttributeComposer l2d() {
        return add(new SimpleInstruction(InstructionConstants.OP_L2D));
    }

    public CompactCodeAttributeComposer f2i() {
        return add(new SimpleInstruction(InstructionConstants.OP_F2I));
    }

    public CompactCodeAttributeComposer f2l() {
        return add(new SimpleInstruction(InstructionConstants.OP_F2L));
    }

    public CompactCodeAttributeComposer f2d() {
        return add(new SimpleInstruction(InstructionConstants.OP_F2D));
    }

    public CompactCodeAttributeComposer d2i() {
        return add(new SimpleInstruction(InstructionConstants.OP_D2I));
    }

    public CompactCodeAttributeComposer d2l() {
        return add(new SimpleInstruction(InstructionConstants.OP_D2L));
    }

    public CompactCodeAttributeComposer d2f() {
        return add(new SimpleInstruction(InstructionConstants.OP_D2F));
    }

    public CompactCodeAttributeComposer i2b() {
        return add(new SimpleInstruction(InstructionConstants.OP_I2B));
    }

    public CompactCodeAttributeComposer i2c() {
        return add(new SimpleInstruction(InstructionConstants.OP_I2C));
    }

    public CompactCodeAttributeComposer i2s() {
        return add(new SimpleInstruction(InstructionConstants.OP_I2S));
    }

    public CompactCodeAttributeComposer lcmp() {
        return add(new SimpleInstruction(InstructionConstants.OP_LCMP));
    }

    public CompactCodeAttributeComposer fcmpl() {
        return add(new SimpleInstruction(InstructionConstants.OP_FCMPL));
    }

    public CompactCodeAttributeComposer fcmpg() {
        return add(new SimpleInstruction(InstructionConstants.OP_FCMPG));
    }

    public CompactCodeAttributeComposer dcmpl() {
        return add(new SimpleInstruction(InstructionConstants.OP_DCMPL));
    }

    public CompactCodeAttributeComposer dcmpg() {
        return add(new SimpleInstruction(InstructionConstants.OP_DCMPG));
    }

    public CompactCodeAttributeComposer ifeq(int branchOffset) {
        return add(new BranchInstruction(InstructionConstants.OP_IFEQ, branchOffset));
    }

    public CompactCodeAttributeComposer ifne(int branchOffset) {
        return add(new BranchInstruction(InstructionConstants.OP_IFNE, branchOffset));
    }

    public CompactCodeAttributeComposer iflt(int branchOffset) {
        return add(new BranchInstruction(InstructionConstants.OP_IFLT, branchOffset));
    }

    public CompactCodeAttributeComposer ifge(int branchOffset) {
        return add(new BranchInstruction(InstructionConstants.OP_IFGE, branchOffset));
    }

    public CompactCodeAttributeComposer ifgt(int branchOffset) {
        return add(new BranchInstruction(InstructionConstants.OP_IFGT, branchOffset));
    }

    public CompactCodeAttributeComposer ifle(int branchOffset) {
        return add(new BranchInstruction(InstructionConstants.OP_IFLE, branchOffset));
    }

    public CompactCodeAttributeComposer ificmpeq(int branchOffset) {
        return add(new BranchInstruction(InstructionConstants.OP_IFICMPEQ, branchOffset));
    }

    public CompactCodeAttributeComposer ificmpne(int branchOffset) {
        return add(new BranchInstruction(InstructionConstants.OP_IFICMPNE, branchOffset));
    }

    public CompactCodeAttributeComposer ificmplt(int branchOffset) {
        return add(new BranchInstruction(InstructionConstants.OP_IFICMPLT, branchOffset));
    }

    public CompactCodeAttributeComposer ificmpge(int branchOffset) {
        return add(new BranchInstruction(InstructionConstants.OP_IFICMPGE, branchOffset));
    }

    public CompactCodeAttributeComposer ificmpgt(int branchOffset) {
        return add(new BranchInstruction(InstructionConstants.OP_IFICMPGT, branchOffset));
    }

    public CompactCodeAttributeComposer ificmple(int branchOffset) {
        return add(new BranchInstruction(InstructionConstants.OP_IFICMPLE, branchOffset));
    }

    public CompactCodeAttributeComposer ifacmpeq(int branchOffset) {
        return add(new BranchInstruction(InstructionConstants.OP_IFACMPEQ, branchOffset));
    }

    public CompactCodeAttributeComposer ifacmpne(int branchOffset) {
        return add(new BranchInstruction(InstructionConstants.OP_IFACMPNE, branchOffset));
    }

    public CompactCodeAttributeComposer goto_(int branchOffset) {
        return add(new BranchInstruction(InstructionConstants.OP_GOTO, branchOffset));
    }

    public CompactCodeAttributeComposer jsr(int branchOffset) {
        return add(new BranchInstruction(InstructionConstants.OP_JSR, branchOffset));
    }

    public CompactCodeAttributeComposer ret(int variableIndex) {
        return add(new VariableInstruction(InstructionConstants.OP_RET, variableIndex));
    }

    public CompactCodeAttributeComposer tableswitch(int defaultOffset, int lowCase, int highCase, int[] jumpOffsets) {
        return add(new TableSwitchInstruction(InstructionConstants.OP_TABLESWITCH, defaultOffset, lowCase, highCase,
            jumpOffsets));
    }

    public CompactCodeAttributeComposer lookupswitch(int defaultOffset, int[] cases, int[] jumpOffsets) {
        return add(
            new LookUpSwitchInstruction(InstructionConstants.OP_LOOKUPSWITCH, defaultOffset, cases, jumpOffsets));
    }

    public CompactCodeAttributeComposer ireturn() {
        return add(new SimpleInstruction(InstructionConstants.OP_IRETURN));
    }

    public CompactCodeAttributeComposer lreturn() {
        return add(new SimpleInstruction(InstructionConstants.OP_LRETURN));
    }

    public CompactCodeAttributeComposer freturn() {
        return add(new SimpleInstruction(InstructionConstants.OP_FRETURN));
    }

    public CompactCodeAttributeComposer dreturn() {
        return add(new SimpleInstruction(InstructionConstants.OP_DRETURN));
    }

    public CompactCodeAttributeComposer areturn() {
        return add(new SimpleInstruction(InstructionConstants.OP_ARETURN));
    }

    public CompactCodeAttributeComposer return_() {
        return add(new SimpleInstruction(InstructionConstants.OP_RETURN));
    }

    public CompactCodeAttributeComposer getstatic(Clazz referencedClass, Member referencedMember) {
        return getstatic(referencedClass.getName(), referencedMember.getName(referencedClass),
            referencedMember.getDescriptor(referencedClass), referencedClass, referencedMember);
    }

    public CompactCodeAttributeComposer getstatic(String className, String name, String descriptor) {
        return getstatic(className, name, descriptor, null, null);
    }

    public CompactCodeAttributeComposer getstatic(String className, String name, String descriptor,
        Clazz referencedClass, Member referencedMember) {
        return getstatic(
            constantPoolEditor.addFieldrefConstant(className, name, descriptor, referencedClass, referencedMember));
    }

    public CompactCodeAttributeComposer getstatic(int constantIndex) {
        return add(new ConstantInstruction(InstructionConstants.OP_GETSTATIC, constantIndex));
    }

    public CompactCodeAttributeComposer putstatic(Clazz referencedClass, Member referencedMember) {
        return putstatic(referencedClass.getName(), referencedMember.getName(referencedClass),
            referencedMember.getDescriptor(referencedClass), referencedClass, referencedMember);
    }

    public CompactCodeAttributeComposer putstatic(String className, String name, String descriptor) {
        return putstatic(className, name, descriptor, null, null);
    }

    public CompactCodeAttributeComposer putstatic(String className, String name, String descriptor,
        Clazz referencedClass, Member referencedMember) {
        return putstatic(
            constantPoolEditor.addFieldrefConstant(className, name, descriptor, referencedClass, referencedMember));
    }

    public CompactCodeAttributeComposer putstatic(int constantIndex) {
        return add(new ConstantInstruction(InstructionConstants.OP_PUTSTATIC, constantIndex));
    }

    public CompactCodeAttributeComposer getfield(String className, String name, String descriptor) {
        return getfield(className, name, descriptor, null, null);
    }

    public CompactCodeAttributeComposer getfield(String className, String name, String descriptor,
        Clazz referencedClass, Member referencedMember) {
        return getfield(
            constantPoolEditor.addFieldrefConstant(className, name, descriptor, referencedClass, referencedMember));
    }

    public CompactCodeAttributeComposer getfield(int constantIndex) {
        return add(new ConstantInstruction(InstructionConstants.OP_GETFIELD, constantIndex));
    }

    public CompactCodeAttributeComposer putfield(String className, String name, String descriptor) {
        return putfield(className, name, descriptor, null, null);
    }

    public CompactCodeAttributeComposer putfield(String className, String name, String descriptor,
        Clazz referencedClass, Member referencedMember) {
        return putfield(
            constantPoolEditor.addFieldrefConstant(className, name, descriptor, referencedClass, referencedMember));
    }

    public CompactCodeAttributeComposer putfield(int constantIndex) {
        return add(new ConstantInstruction(InstructionConstants.OP_PUTFIELD, constantIndex));
    }

    public CompactCodeAttributeComposer invokevirtual(String className, String name, String descriptor) {
        return invokevirtual(className, name, descriptor, null, null);
    }

    public CompactCodeAttributeComposer invokevirtual(Clazz referencedClass, Member referencedMember) {
        return invokevirtual(referencedClass.getName(), referencedMember.getName(referencedClass),
            referencedMember.getDescriptor(referencedClass), referencedClass, referencedMember);
    }

    public CompactCodeAttributeComposer invokevirtual(String className, String name, String descriptor,
        Clazz referencedClass, Member referencedMember) {
        return invokevirtual(
            constantPoolEditor.addMethodrefConstant(className, name, descriptor, referencedClass, referencedMember));
    }

    public CompactCodeAttributeComposer invokevirtual(int constantIndex) {
        return add(new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, constantIndex));
    }

    public CompactCodeAttributeComposer invokespecial(String className, String name, String descriptor) {
        return invokespecial(className, name, descriptor, null, null);
    }

    public CompactCodeAttributeComposer invokespecial(String className, String name, String descriptor,
        Clazz referencedClass, Member referencedMember) {
        return invokespecial(
            constantPoolEditor.addMethodrefConstant(className, name, descriptor, referencedClass, referencedMember));
    }

    public CompactCodeAttributeComposer invokespecial(int constantIndex) {
        return add(new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, constantIndex));
    }

    public CompactCodeAttributeComposer invokestatic(String className, String name, String descriptor) {
        return invokestatic(className, name, descriptor, null, null);
    }

    public CompactCodeAttributeComposer invokestatic(Clazz referencedClass, Member referencedMember) {
        return invokestatic(referencedClass.getName(), referencedMember.getName(referencedClass),
            referencedMember.getDescriptor(referencedClass), referencedClass, referencedMember);
    }

    public CompactCodeAttributeComposer invokestatic(String className, String name, String descriptor,
        Clazz referencedClass, Member referencedMember) {
        return invokestatic(
            constantPoolEditor.addMethodrefConstant(className, name, descriptor, referencedClass, referencedMember));
    }

    public CompactCodeAttributeComposer invokestaticinterface(String className, String name, String descriptor) {
        return invokestaticinterface(className, name, descriptor, null, null);
    }

    public CompactCodeAttributeComposer invokestaticinterface(Clazz referencedClass, Member referencedMember) {
        return invokestaticinterface(referencedClass.getName(), referencedMember.getName(referencedClass),
            referencedMember.getDescriptor(referencedClass), referencedClass, referencedMember);
    }

    public CompactCodeAttributeComposer invokestaticinterface(String className, String name, String descriptor,
        Clazz referencedClass, Member referencedMember) {
        return invokestatic(constantPoolEditor.addInterfaceMethodrefConstant(className, name, descriptor,
            referencedClass, referencedMember));
    }

    public CompactCodeAttributeComposer invokestatic(int constantIndex) {
        return add(new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, constantIndex));
    }

    public CompactCodeAttributeComposer invokeinterface(String className, String name, String descriptor) {
        return invokeinterface(className, name, descriptor, null, null);
    }

    public CompactCodeAttributeComposer invokeinterface(String className, String name, String descriptor,
        Clazz referencedClass, Member referencedMember) {
        int invokeinterfaceConstant = (ClassUtil.internalMethodParameterSize(descriptor, false)) << 8;

        return invokeinterface(constantPoolEditor.addInterfaceMethodrefConstant(className, name, descriptor,
            referencedClass, referencedMember), invokeinterfaceConstant);
    }

    public CompactCodeAttributeComposer invokeinterface(int constantIndex, int constant) {
        return add(new ConstantInstruction(InstructionConstants.OP_INVOKEINTERFACE, constantIndex, constant));
    }

    public CompactCodeAttributeComposer invokedynamic(int bootStrapMethodIndex, String name, String descriptor,
        Clazz[] referencedClasses) {
        return invokedynamic(
            constantPoolEditor.addInvokeDynamicConstant(bootStrapMethodIndex, name, descriptor, referencedClasses));
    }

    public CompactCodeAttributeComposer invokedynamic(int constantIndex) {
        return add(new ConstantInstruction(InstructionConstants.OP_INVOKEDYNAMIC, constantIndex));
    }

    public CompactCodeAttributeComposer new_(String className) {
        return new_(className, null);
    }

    public CompactCodeAttributeComposer new_(String className, Clazz referencedClass) {
        return new_(constantPoolEditor.addClassConstant(className, referencedClass));
    }

    public CompactCodeAttributeComposer new_(int constantIndex) {
        return add(new ConstantInstruction(InstructionConstants.OP_NEW, constantIndex));
    }

    public CompactCodeAttributeComposer newarray(int constant) {
        return add(new SimpleInstruction(InstructionConstants.OP_NEWARRAY, constant));
    }

    public CompactCodeAttributeComposer anewarray(String className, Clazz referencedClass) {
        return anewarray(constantPoolEditor.addClassConstant(className, referencedClass));
    }

    public CompactCodeAttributeComposer anewarray(int constantIndex) {
        return add(new ConstantInstruction(InstructionConstants.OP_ANEWARRAY, constantIndex));
    }

    public CompactCodeAttributeComposer arraylength() {
        return add(new SimpleInstruction(InstructionConstants.OP_ARRAYLENGTH));
    }

    public CompactCodeAttributeComposer athrow() {
        return add(new SimpleInstruction(InstructionConstants.OP_ATHROW));
    }

    public CompactCodeAttributeComposer checkcast(String className) {
        return checkcast(className, null);
    }

    public CompactCodeAttributeComposer checkcast(String className, Clazz referencedClass) {
        return checkcast(constantPoolEditor.addClassConstant(className, referencedClass));
    }

    public CompactCodeAttributeComposer checkcast(int constantIndex) {
        return add(new ConstantInstruction(InstructionConstants.OP_CHECKCAST, constantIndex));
    }

    public CompactCodeAttributeComposer instanceof_(String className, Clazz referencedClass) {
        return instanceof_(constantPoolEditor.addClassConstant(className, referencedClass));
    }

    public CompactCodeAttributeComposer instanceof_(int constantIndex) {
        return add(new ConstantInstruction(InstructionConstants.OP_INSTANCEOF, constantIndex));
    }

    public CompactCodeAttributeComposer monitorenter() {
        return add(new SimpleInstruction(InstructionConstants.OP_MONITORENTER));
    }

    public CompactCodeAttributeComposer monitorexit() {
        return add(new SimpleInstruction(InstructionConstants.OP_MONITOREXIT));
    }

    public CompactCodeAttributeComposer wide() {
        return add(new SimpleInstruction(InstructionConstants.OP_WIDE));
    }

    public CompactCodeAttributeComposer multianewarray(String className, Clazz referencedClass) {
        return multianewarray(constantPoolEditor.addClassConstant(className, referencedClass));
    }

    public CompactCodeAttributeComposer multianewarray(int constantIndex) {
        return add(new ConstantInstruction(InstructionConstants.OP_MULTIANEWARRAY, constantIndex));
    }

    public CompactCodeAttributeComposer ifnull(int branchOffset) {
        return add(new BranchInstruction(InstructionConstants.OP_IFNULL, branchOffset));
    }

    public CompactCodeAttributeComposer ifnonnull(int branchOffset) {
        return add(new BranchInstruction(InstructionConstants.OP_IFNONNULL, branchOffset));
    }

    public CompactCodeAttributeComposer goto_w(int branchOffset) {
        return add(new BranchInstruction(InstructionConstants.OP_GOTO_W, branchOffset));
    }

    public CompactCodeAttributeComposer jsr_w(int branchOffset) {
        return add(new BranchInstruction(InstructionConstants.OP_JSR_W, branchOffset));
    }

    // Additional convenience methods.

    /**
     * Pushes the given primitive value on the stack.
     *
     * Operand stack: ... -> ..., value
     *
     * @param primitive the primitive value to be pushed - should never be null.
     * @param internalType the internal type of the primitive ('Z','B','I',...)
     */
    public CompactCodeAttributeComposer pushPrimitive(Object primitive, char internalType) {
        switch (internalType) {
            case TYPE_BOOLEAN:
                return ((Boolean)primitive).booleanValue() ? iconst_1() : iconst_0();
            case TYPE_BYTE:
                return bipush((Byte)primitive);
            case TYPE_CHAR:
                return ldc(((Character)primitive).charValue());
            case TYPE_SHORT:
                return sipush((Short)primitive);
            case TYPE_INT:
                return ldc(((Integer)primitive).intValue());
            case TYPE_LONG:
                return ldc2_w((Long)primitive);
            case TYPE_FLOAT:
                return ldc(((Float)primitive).floatValue());
            case TYPE_DOUBLE:
                return ldc2_w((Double)primitive);
            default:
                throw new IllegalArgumentException(primitive.toString());
        }
    }

    /**
     * Pushes the given primitive int on the stack in the most efficient way (as an iconst, bipush, sipush, or ldc
     * instruction).
     *
     * @param value the int value to be pushed.
     */
    public CompactCodeAttributeComposer pushInt(int value) {
        return value >= -1 && value <= 5 ? iconst(value)
            : value == (byte)value ? bipush(value) : value == (short)value ? sipush(value) : ldc(value);
    }

    /**
     * Pushes the given primitive float on the stack in the most efficient way (as an fconst or ldc instruction).
     *
     * @param value the int value to be pushed.
     */
    public CompactCodeAttributeComposer pushFloat(float value) {
        return value == 0f || value == 1f ? fconst((int)value) : ldc(value);
    }

    /**
     * Pushes the given primitive long on the stack in the most efficient way (as an lconst or ldc instruction).
     *
     * @param value the int value to be pushed.
     */
    public CompactCodeAttributeComposer pushLong(long value) {
        return value == 0L || value == 1L ? lconst((int)value) : ldc2_w(value);
    }

    /**
     * Pushes the given primitive double on the stack in the most efficient way (as a dconst or ldc instruction).
     *
     * @param value the int value to be pushed.
     */
    public CompactCodeAttributeComposer pushDouble(double value) {
        return value == 0. || value == 1. ? dconst((int)value) : ldc2_w(value);
    }

    /**
     * Pushes a new array on the stack.
     *
     * Operand stack: ... -> ..., array
     *
     * @param elementTypeOrClassName the array element type (or class name in case of objects).
     * @param size the size of the array to be created.
     */
    public CompactCodeAttributeComposer pushNewArray(String elementTypeOrClassName, int size) {
        // Create new array.
        pushInt(size);

        return ClassUtil.isInternalPrimitiveType(elementTypeOrClassName)
            ? newarray(InstructionUtil.arrayTypeFromInternalType(elementTypeOrClassName.charAt(0)))
            : anewarray(elementTypeOrClassName, null);
    }

    /**
     * Loads the given variable onto the stack.
     *
     * Operand stack: ... -> ..., value
     *
     * @param variableIndex the index of the variable to be loaded.
     * @param internalType the type of the variable to be loaded.
     */
    public CompactCodeAttributeComposer load(int variableIndex, String internalType) {
        return load(variableIndex, internalType.charAt(0));
    }

    /**
     * Loads the given variable of primitive type onto the stack.
     *
     * Operand stack: ... -> ..., value
     *
     * @param variableIndex the index of the variable to be loaded.
     * @param internalType the primitive type of the variable to be loaded.
     */
    public CompactCodeAttributeComposer load(int variableIndex, char internalType) {
        switch (internalType) {
            case TYPE_BOOLEAN:
            case TYPE_BYTE:
            case TYPE_CHAR:
            case TYPE_SHORT:
            case TYPE_INT:
                return iload(variableIndex);
            case TYPE_LONG:
                return lload(variableIndex);
            case TYPE_FLOAT:
                return fload(variableIndex);
            case TYPE_DOUBLE:
                return dload(variableIndex);
            default:
                return aload(variableIndex);
        }
    }

    /**
     * Stores the value on top of the stack in the variable with given index.
     *
     * Operand stsack: ..., value -> ...
     *
     * @param variableIndex the index of the variable where to store the value.
     * @param internalType the type of the value to be stored.
     */
    public CompactCodeAttributeComposer store(int variableIndex, String internalType) {
        return store(variableIndex, internalType.charAt(0));
    }

    /**
     * Stores the primitve value on top of the stack in the variable with given index.
     *
     * Operand stack: ..., value -> ...
     *
     * @param variableIndex the index of the variable where to store the value.
     * @param internalType the primitive type of the value to be stored.
     */
    public CompactCodeAttributeComposer store(int variableIndex, char internalType) {
        switch (internalType) {
            case TYPE_BOOLEAN:
            case TYPE_BYTE:
            case TYPE_CHAR:
            case TYPE_SHORT:
            case TYPE_INT:
                return istore(variableIndex);
            case TYPE_LONG:
                return lstore(variableIndex);
            case TYPE_FLOAT:
                return fstore(variableIndex);
            case TYPE_DOUBLE:
                return dstore(variableIndex);
            default:
                return astore(variableIndex);
        }
    }

    /**
     * Stores an element to an array.
     *
     * Operand stack: ..., array, index, value -> ...
     *
     * @param elementType the type of the value to be stored.
     */
    public CompactCodeAttributeComposer storeToArray(String elementType) {
        switch (elementType.charAt(0)) {
            case TYPE_BOOLEAN:
            case TYPE_BYTE:
                return bastore();
            case TYPE_CHAR:
                return castore();
            case TYPE_SHORT:
                return sastore();
            case TYPE_INT:
                return iastore();
            case TYPE_LONG:
                return lastore();
            case TYPE_FLOAT:
                return fastore();
            case TYPE_DOUBLE:
                return dastore();
            default:
                return aastore();
        }
    }

    /**
     * Appends the proper return statement for the given internal type.
     *
     * @param internalType the return type.
     */
    public CompactCodeAttributeComposer return_(String internalType) {
        switch (internalType.charAt(0)) {
            case TYPE_BOOLEAN:
            case TYPE_BYTE:
            case TYPE_CHAR:
            case TYPE_SHORT:
            case TYPE_INT:
                return ireturn();
            case TYPE_LONG:
                return lreturn();
            case TYPE_FLOAT:
                return freturn();
            case TYPE_DOUBLE:
                return dreturn();
            case TYPE_VOID:
                return return_();
            default:
                return areturn();
        }
    }

    /**
     * Appends instructions to print out the given message and the top int on the stack.
     */
    public CompactCodeAttributeComposer appendPrintIntegerInstructions(String message) {
        appendPrintInstructions(message);
        appendPrintIntegerInstructions();
        return this;
    }

    /**
     * Appends instructions to print out the given message and the top int on the stack as a hexadecimal value.
     */
    public CompactCodeAttributeComposer appendPrintIntegerHexInstructions(String message) {
        appendPrintInstructions(message);
        appendPrintIntegerHexInstructions();
        return this;
    }

    /**
     * Appends instructions to print out the given message and the top long on the stack.
     */
    public CompactCodeAttributeComposer appendPrintLongInstructions(String message) {
        appendPrintInstructions(message);
        appendPrintLongInstructions();
        return this;
    }

    /**
     * Appends instructions to print out the given message and the top String on the stack.
     */
    public CompactCodeAttributeComposer appendPrintStringInstructions(String message) {
        appendPrintInstructions(message);
        appendPrintStringInstructions();
        return this;
    }

    /**
     * Appends instructions to print out the given message and the top Object on the stack.
     */
    public CompactCodeAttributeComposer appendPrintObjectInstructions(String message) {
        appendPrintInstructions(message);
        appendPrintObjectInstructions();
        return this;
    }

    /**
     * Appends instructions to print out the given message and the stack trace of the top Throwable on the stack.
     */
    public CompactCodeAttributeComposer appendPrintStackTraceInstructions(String message) {
        appendPrintInstructions(message);
        appendPrintStackTraceInstructions();
        return this;
    }

    /**
     * Appends instructions to print out the given message.
     */
    public CompactCodeAttributeComposer appendPrintInstructions(String message) {
        getstatic("java/lang/System", "err", "Ljava/io/PrintStream;");
        ldc(message);
        invokevirtual("java/io/PrintStream", "println", "(Ljava/lang/String;)V");
        return this;
    }

    /**
     * Appends instructions to print out the top int on the stack.
     */
    public CompactCodeAttributeComposer appendPrintIntegerInstructions() {
        dup();
        getstatic("java/lang/System", "err", "Ljava/io/PrintStream;");
        swap();
        invokevirtual("java/io/PrintStream", "println", "(I)V");
        return this;
    }

    /**
     * Appends instructions to print out the top integer on the stack as a hexadecimal value.
     */
    public CompactCodeAttributeComposer appendPrintIntegerHexInstructions() {
        dup();
        getstatic("java/lang/System", "err", "Ljava/io/PrintStream;");
        swap();
        invokestatic("java/lang/Integer", "toHexString", "(I)Ljava/lang/String;");
        invokevirtual("java/io/PrintStream", "println", "(Ljava/lang/String;)V");
        return this;
    }

    /**
     * Appends instructions to print out the top long on the stack.
     */
    public CompactCodeAttributeComposer appendPrintLongInstructions() {
        dup2();
        getstatic("java/lang/System", "err", "Ljava/io/PrintStream;");
        dup_x2();
        pop();
        invokevirtual("java/io/PrintStream", "println", "(J)V");
        return this;
    }

    /**
     * Appends instructions to print out the top String on the stack.
     */
    public CompactCodeAttributeComposer appendPrintStringInstructions() {
        dup();
        getstatic("java/lang/System", "err", "Ljava/io/PrintStream;");
        swap();
        invokevirtual("java/io/PrintStream", "println", "(Ljava/lang/String;)V");
        return this;
    }

    /**
     * Appends instructions to print out the top Object on the stack.
     */
    public CompactCodeAttributeComposer appendPrintObjectInstructions() {
        dup();
        getstatic("java/lang/System", "err", "Ljava/io/PrintStream;");
        swap();
        invokevirtual("java/io/PrintStream", "println", "(Ljava/lang/Object;)V");
        return this;
    }

    /**
     * Appends instructions to print out the stack trace of the top Throwable on the stack.
     */
    public CompactCodeAttributeComposer appendPrintStackTraceInstructions() {
        dup();
        invokevirtual("java/lang/Throwable", "printStackTrace", "()V");
        return this;
    }

    // Implementations for AttributeVisitor.

    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}

    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) {
        codeAttributeComposer.visitCodeAttribute(clazz, method, codeAttribute);
    }

    // Small utility methods.

    /**
     * Adds the given instruction, shrinking it if necessary.
     */
    private CompactCodeAttributeComposer add(Instruction instruction) {
        codeAttributeComposer.appendInstruction(instruction);

        return this;
    }

    public static void main(String[] args) {
        ProgramClass targetClass = new ProgramClass(0, 0, new Constant[32], 0, 0, 0);

        CompactCodeAttributeComposer composer = new CompactCodeAttributeComposer(targetClass);

        composer.beginCodeFragment(4);
        composer.appendInstruction(0, new SimpleInstruction(InstructionConstants.OP_ICONST_0));
        composer.appendInstruction(1, new VariableInstruction(InstructionConstants.OP_ISTORE, 0));
        composer.appendInstruction(2, new BranchInstruction(InstructionConstants.OP_GOTO, 1));

        composer.beginCodeFragment(4);
        composer.appendInstruction(0, new VariableInstruction(InstructionConstants.OP_IINC, 0, 1));
        composer.appendInstruction(1, new VariableInstruction(InstructionConstants.OP_ILOAD, 0));
        composer.appendInstruction(2, new SimpleInstruction(InstructionConstants.OP_ICONST_5));
        composer.appendInstruction(3, new BranchInstruction(InstructionConstants.OP_IFICMPLT, -3));
        composer.endCodeFragment();

        composer.appendInstruction(3, new SimpleInstruction(InstructionConstants.OP_RETURN));
        composer.endCodeFragment();
    }
}
